﻿using System;
using System.Linq;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Sklep.Domain.Abstract;
using Sklep.Domain.Entities;
using Sklep.WebUI.Controllers;
using Sklep.WebUI.HtmlHelpers;
using Sklep.WebUI.Models;

namespace Sklep.UnitTests
{
    [TestClass]
    public class Test
    {
        [TestMethod]
        public void CanPaginate()
        {
            var mock = new Mock<IProductRepository>();
            mock.Setup(m => m.Products).Returns(new[]
                {
                    new Product {ProductID = 1, Name = "P1"},
                    new Product {ProductID = 2, Name = "P2"},
                    new Product {ProductID = 3, Name = "P3"},
                    new Product {ProductID = 4, Name = "P4"},
                    new Product {ProductID = 5, Name = "P5"}
                }.AsQueryable());

            var controller = new ProductController(mock.Object) {PageSize = 3};

            //dzialanie
            var results = (ProductListViewModel) controller.List(null, 2).Model;

            //assercje
            Product[] prodArray = results.Products.ToArray();
            Assert.IsTrue(prodArray.Length == 2);
            Assert.AreEqual(prodArray[0].Name, "P4");
            Assert.AreEqual(prodArray[1].Name, "P5");
        }

        [TestMethod]
        public void CanGeneratePageLinks()
        {
            //przygotowanie - definiowanie metody pomocniczej HTML - potrzebujemy tego
            //aby uzyc metody rozszerzajacej
            HtmlHelper myHelper = null;

            //przygotowania - tworzenie danych PagingInfo
            var info = new PagingInfo {CurrentPage = 2, TotalItems = 28, ItemsPerPage = 10};

            //przygotowania konfiguracja delegata z wyrazeniem labda
            Func<int, string> pageUrlDelegate = i => "Strona" + i;

            //dzialanie
            MvcHtmlString result = myHelper.PageLinks(info, pageUrlDelegate);

            //asercje
            Assert.AreEqual(result.ToString(),
                            @"<a href=""Strona1"">1</a><a class=""selected"" href=""Strona2"">2</a><a href=""Strona3"">3</a>");
        }

        [TestMethod]
        public void CanSendPaginationViewModel()
        {
            //tworzenie imitacji repozytorium
            var mock = new Mock<IProductRepository>();
            mock.Setup(m => m.Products).Returns(new[]
                {
                    new Product {ProductID = 1, Name = "P1"},
                    new Product {ProductID = 2, Name = "P2"},
                    new Product {ProductID = 3, Name = "P3"},
                    new Product {ProductID = 4, Name = "P4"},
                    new Product {ProductID = 5, Name = "P5"}
                }.AsQueryable());

            //przygotowanie Tworzenia Kontrolera i ustawianie 3 elementowej strony
            var controller = new ProductController(mock.Object) {PageSize = 3};

            //dzialanie
            var result = (ProductListViewModel) controller.List(null, 2).Model;

            //asercje
            PagingInfo pageInfo = result.PagingInfo;
            Assert.AreEqual(pageInfo.CurrentPage, 2);
            Assert.AreEqual(pageInfo.ItemsPerPage, 3);
            Assert.AreEqual(pageInfo.TotalItems, 5);
            Assert.AreEqual(pageInfo.TotalPages, 2);
        }

        [TestMethod]
        public void CanFilterProducts()
        {
            //przygotowanie
            //stworzenie imitacji repozytorium
            var mock = new Mock<IProductRepository>();
            mock.Setup(m => m.Products).Returns(new[]
                {
                    new Product {ProductID = 1, Name = "P1", Category = "Cat1"},
                    new Product {ProductID = 2, Name = "P2", Category = "Cat2"},
                    new Product {ProductID = 3, Name = "P3", Category = "Cat1"},
                    new Product {ProductID = 4, Name = "P4", Category = "Cat2"},
                    new Product {ProductID = 5, Name = "P5", Category = "Cat3"}
                }.AsQueryable());

            //przygotowanie stworzenia kontrolera i ustawienie 3-elementowej strony
            var controller = new ProductController(mock.Object) {PageSize = 3};

            //dzialanie
            Product[] result = ((ProductListViewModel) controller.List("Cat2").Model).Products.ToArray();

            //asercje
            Assert.AreEqual(result.Length, 2);
            Assert.IsTrue(result[0].Name == "P2" && result[0].Category == "Cat2");
            Assert.IsTrue(result[1].Name == "P4" && result[1].Category == "Cat2");
        }

        [TestMethod]
        public void CanAddNewLines()
        {
            var p1 = new Product {ProductID = 1, Name = "P1"};
            var p2 = new Product {ProductID = 2, Name = "P2"};

            //przygotowanie i utwozenie koszyka
            var target = new Cart();

            //dzialanie
            target.AddItem(p1, 1);
            target.AddItem(p2, 1);

            CartLine[] lines = target.Lines.ToArray();

            //asercje
            Assert.AreEqual(lines.Length, 2);
            Assert.AreEqual(lines[0].Product, p1);
            Assert.AreEqual(lines[1].Product, p2);
        }


        [TestMethod]
        public void CanAddQuantityForExistingLines()
        {
            var p1 = new Product {ProductID = 1, Name = "P1"};
            var p2 = new Product {ProductID = 2, Name = "P2"};

            //utworzenie koszyka

            var target = new Cart();

            target.AddItem(p1, 1);
            target.AddItem(p2, 1);
            target.AddItem(p1, 10);

            CartLine[] results = target.Lines.ToArray();

            Assert.AreEqual(results.Length, 2);
            Assert.AreEqual(results[0].Quantity, 11);
            Assert.AreEqual(results[1].Quantity, 1);
        }

        [TestMethod]
        public void CanRemoveLine()
        {
            var p1 = new Product {ProductID = 1, Name = "P1"};
            var p2 = new Product {ProductID = 2, Name = "P2"};
            var p3 = new Product {ProductID = 3, Name = "P3"};

            var target = new Cart();

            target.AddItem(p1, 1);
            target.AddItem(p2, 3);
            target.AddItem(p3, 5);
            target.AddItem(p2, 1);


            //dzialanie 
            target.RemoveLine(p2);

            Assert.AreEqual(target.Lines.Count(c => c.Product == p2), 0);
            Assert.AreEqual(target.Lines.Count(), 2);
        }

        [TestMethod]
        public void CalculateCartTotal()
        {
            var p1 = new Product {ProductID = 1, Name = "P1", Price = 100M};
            var p2 = new Product {ProductID = 2, Name = "P2", Price = 50M};

            //przygotowanie - utworzenie nowgeo koszyka

            var target = new Cart();

            //dzialanie
            target.AddItem(p1, 1);
            target.AddItem(p2, 1);
            target.AddItem(p1, 3);
            decimal result = target.ComputeTotalValue();

            Assert.AreEqual(result, 450M);
        }

        [TestMethod]
        public void CanAddToCart()
        {
            //przygotowanie - Tworzenie imitacji repozytorium
            var mock = new Mock<IProductRepository>();
            mock.Setup(m => m.Products).Returns(new[]
                {
                    new Product {ProductID = 1, Name = "P1", Category = "Apples"}
                }.AsQueryable());

            //przygotowanie - utworzenie koszyka
            var cart = new Cart();

            //przygotowanie utworzenie kontrolera
            var target = new CartController(mock.Object, null);

            //dzialanie dodanie produktu do koszyka
            target.AddToCart(cart, 1, null);

            //Asercje
            Assert.AreEqual(cart.Lines.Count(), 1);
            Assert.AreEqual(cart.Lines.ToArray()[0].Product.ProductID, 1);
        }

        [TestMethod]
        public void CannotCheckoutEmptyCart()
        {
            //przygotowanie i tworzenie imitacji procesora zamowien
            var mock = new Mock<IOrderProcessor>();

            //przygptowanie  tworzenie pustego koszyka
            var cart = new Cart();

            //przygotowanie Tworzenie danych do wyslki
            var shippingDetails = new ShippingDetails();

            //przygotowanie - Tworzenie instancji kontrolera
            var target = new CartController(null, mock.Object);

            //dzialanie 
            ViewResult result = target.Checkout(cart, shippingDetails);

            //asercje sprawdzenie czy zamowienie zostalo przekazane do procesora
            mock.Verify(m => m.ProcessOrder(It.IsAny<Cart>(), It.IsAny<ShippingDetails>()),
                        Times.Never());

            //asercje sprawdzenie czy metoda zwraca domyslny widok
            Assert.AreEqual("", result.ViewName);

            Assert.AreEqual(false, result.ViewData.ModelState.IsValid);
        }

        [TestMethod]
        public void CannotCheckoutInvalidShippingDetails()
        {
            var mock = new Mock<IOrderProcessor>();

            //przygotowanie - tworzenie koszyka z produktem
            var cart = new Cart();
            cart.AddItem(new Product(), 1);

            //Przygotowanie - Tworzenie instancji kontrolera
            var target = new CartController(null, mock.Object);

            //przygotowanie - dodanie bledu do modelu
            target.ModelState.AddModelError("error", "error");

            //dzialanie - proba zakonczenia zamowienia
            ViewResult result = target.Checkout(cart, new ShippingDetails());

            //asercje - sprawdzenie czy zamowienie nie zostalo przekazane do procesora
            mock.Verify(m => m.ProcessOrder(It.IsAny<Cart>(), It.IsAny<ShippingDetails>()), Times.Never());

            //asercje - spr czy metoda zwraca domyslny widok
            Assert.AreEqual("", result.ViewName);

            //asercje - spr czy przekazujemy nieprawidlowy model do widoku
            Assert.AreEqual(false, result.ViewData.ModelState.IsValid);
        }

        [TestMethod]
        public void CanCheckoutAndSubmit()
        {
            //przygotowanie - Tworzenie imitacji procesora zamowien
            var mock = new Mock<IOrderProcessor>();

            //przygotowanie - tworzenie koszyka z produktem
            var cart = new Cart();
            cart.AddItem(new Product(), 1);

            //przygotowanie tworzenie instancji kontrolera
            var target = new CartController(null, mock.Object);

            //dzialanie - proba zakonczenia zlecenia
            ViewResult result = target.Checkout(cart, new ShippingDetails());

            //asercje
            mock.Verify(m => m.ProcessOrder(It.IsAny<Cart>(), It.IsAny<ShippingDetails>()), Times.Once());

            Assert.AreEqual("Completed", result.ViewName);

            //asercje
            Assert.AreEqual(true, result.ViewData.ModelState.IsValid);
        }
    }
}